﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Atlepage
{
    /// <summary>
    /// This class provides a simple way to work with named numeric
    /// constants. This class exists because .net does not handle
    /// enum types as generic parameters in a way that is very useful.
    /// </summary>
    /// <remarks>
    /// It is important to note that the numeric constants must be 
    /// greater than or equal to zero
    /// </remarks>
    public interface IEnum
    {
        /// <summary>
        /// This returns a list of names. The order is not required to be the
        /// same as the numeric values associated with them.
        /// </summary>
        string[] Names { get; }
        /// <summary>
        /// This returns the maximum value that is associated with a name.
        /// </summary>
        int MaximumValue { get; }
        /// <summary>
        /// This method return true if the given name has a value associated with it.
        /// </summary>
        /// <param name="s">The name to look for</param>
        /// <returns>true if the name exits</returns>
        bool Contains(string s);
        /// <summary>
        /// Looks up the given string and returns the numeric value for it.
        /// </summary>
        /// <param name="key">the name to look for</param>
        /// <exception cref="System.IndexOutOfRange">This is thrown if the name is not found</exception>
        /// <returns>A positive integer</returns>
        int this[string key] { get; }
        /// <summary>
        /// This method tries to find the value associated with the string.
        /// </summary>
        /// <param name="key">The name to look for</param>
        /// <param name="val">If the method returns true, this is the value associated with the name.</param>
        /// <returns>true if a value was found</returns>
        bool TryGetValue(string key, out int val);
    }

    /// <summary>
    /// This class provides an efficient implementation of the <see cref="Atlepage.IEnum"/> interface, based
    /// on an enum.
    /// </summary>
    /// <remarks>
    /// This class stores two arrays: the names and the values. They are sorted by the names.
    /// The sorting allows for quick binary searches, so a lookup is always log(n).
    /// </remarks>
    /// <typeparam name="TEnum">The type of an enum.</typeparam>
    public class GenericEnum<TEnum> : IEnum
        where TEnum : IConvertible
    {
        int[] values;
        string[] names;
        int max;

        public GenericEnum()
        {
            values = (from t in (TEnum[])Enum.GetValues(typeof(TEnum)) select t.ToInt32(null)).ToArray();
            names = (from v in values select string.Intern(Enum.GetName(typeof(TEnum), v))).ToArray();
            Array.Sort<string, int>(names, values);
            max = values.Max();
        }

        public bool Contains(string key) { return LookUp(key) >= 0; }
        public int this[string key] { get { return values[LookUp(key)]; } }
        public string[] Names { get { return names; } }

        public bool TryGetValue(string key, out int val)
        {
            val = 0;
            int i = LookUp(key);
            if (i < 0)
                return false;
            val = values[i];
            return true;
        }

        private int LookUp(string key)
        {
            return Array.BinarySearch<string>(names, key);
        }

        public int MaximumValue
        {
            get { return max; }
        }
    }
}
